/* ------------------------------------------------------------------------*
 *
 * ------------------------------------------------------------------------*/
#include "os_obj.h"
#include "bsp_spi_nor.h"
#include "nor_cmd.h"
#include "data_map.h"
#include "lut\nand_lut.h"
#include "stdlib.h"
#include "string.h"
#include "common.h"
#include "sys_task.h"
#include "rtctime.h"
#include "flashtoeep.h"
#include "common.h"
#include "str_ext.h"
#include "bsp_api.h"

void nand_lut_write_all(void);
void nand_lut_read_all(void);
void nand_spi_check(void);
void nand_qpi_check(void);
void nand_lut_check(void);

char nand_fmc_dev_link(nand_lut_type *lut);
char nand_spi_dev_link(nand_lut_type *lut);

nand_lut_type nand_dev;
nand_lut_api_make(nand_dev);
#define nlut &nand_dev

/****************************************************************************
* @brief   delay .
*****************************************************************************/

typedef struct
{
	uint16_t wr_req;	// 从page指定页开始写入数据dat，如果页非空跳过
	uint16_t rd_req;	// 从page指定页开始读取数据
	uint16_t er_req;	// 从blocks指定块开始擦除块
	uint16_t lut_req;	// 重建LUT
	uint16_t check_req; // 0空闲 1-spi 2-qpi 3-lut 全写全读验证
	uint16_t cmd_req;
	uint32_t lut_page_rd_req;

	uint8_t nand_fill_req; // 全盘写入数据-lut
	uint8_t nand_read_req; // 读取全盘数据-lut

	uint8_t *wr_buff;
	uint8_t *rd_buff;
	dword_tt *dw_buff;

	uint32_t blocks;
	uint32_t pages;
	uint32_t len;

	uint32_t err;
	uint32_t dat;
	uint32_t opt_time;

	uint8_t ecc_err;
	char quit;
} nand_test_type;

nand_test_type nand_tst;

/*-----------------------------------------------------------------------------------------------------*
* nand lut write data get
*-----------------------------------------------------------------------------------------------------*/
uint8_t nand_fill_dat_get(nand_lut_type *lut, uint32_t addr_offset, uint8_t *pdat, uint16_t len)
{
	uint32_t *wdat;
	uint32_t dat;

	wdat = (uint32_t *)pdat;

	dat = nand_tst.dat + addr_offset;

	len >>= 2;
	while (len--)
	{
		*wdat++ = dat;
		dat += 4;
	}

	return 0;
}

/*-----------------------------------------------------------------------------------------------------*
* nand lut write data get for lut check
*-----------------------------------------------------------------------------------------------------*/
uint8_t nand_check_dat_get(nand_lut_type *lut,uint32_t addr_offset, uint8_t *pdat, uint16_t len)
{
	uint32_t *wdat;
	uint32_t dat;

	wdat = (uint32_t *)pdat;

	dat = nand_tst.dat + addr_offset;

	len >>= 2;
	while (len--)
	{
		*wdat++ = dat;
		dat += 4;
	}

	return 0;
}

/*------------------------------------------------------------------------------*
* nand test
*-------------------------------------------------------------------------------*/
void nand_flash_test(void)
{
	flash_dbg_print("switch to nand test module \n");

	// 申请内存
	nand_tst.wr_buff = malloc(2048 + 16);
	nand_tst.rd_buff = malloc(2048 + 16);
	nand_tst.dw_buff = malloc(4096 + 16);

	// 初始化
	nand_tst.dat = 0x80;
	nand_tst.pages = 1;
	nand_tst.len = 1;
	nand_tst.wr_req = 0;

	// lut 初始化
	nand_lut_api_link(nand_dev);
	nand_lut_open(nlut, nand_fmc_dev_link);
	//nand_lut_open(nlut, nand_spi_dev_link);

	nand_tst.lut_page_rd_req = INT32_MAX;

	for (;;)
	{
		osDelay(10);

		//写入指定物理页
		while (nand_tst.wr_req)
		{
			uint16_t bempty = 1;
			//读取判定
			nand_dev.hal.read_page(nand_tst.pages);
			nand_dev.hal.read_cache(0, nand_tst.rd_buff, 2048);
			if (0 != comp_mem_dat_u8(nand_tst.rd_buff, 0xFF, 2048))
				bempty = 0;

			if (bempty)
			{
				memset(nand_tst.wr_buff, nand_tst.dat, 2048);
				nand_dev.hal.write_cache(0, nand_tst.wr_buff, 2048);
				nand_dev.hal.program_exe(nand_tst.pages);
			}

			nand_tst.wr_req--;
			nand_tst.pages++;
			nand_tst.dat++;
		}

		//读取指定物理页
		while (nand_tst.rd_req)
		{
			nand_dev.hal.read_page(nand_tst.pages);
			nand_tst.ecc_err = nand_dev.hal.ecc_error_get();
			nand_dev.hal.read_cache(0, nand_tst.rd_buff, 2048);
			nand_tst.rd_req--;
			nand_tst.pages++;
		}

		//擦除指定物理块
		while (nand_tst.er_req)
		{
			nand_dev.hal.block_erase(nand_tst.blocks);
			nand_tst.er_req--;
			nand_tst.blocks++;
		}

		// 读取指定LUT页
		if (nand_tst.lut_page_rd_req != INT32_MAX)
		{
			nand_lut_read_init(nlut);
			nand_lut_page_read(nlut, nand_tst.lut_page_rd_req, nand_tst.rd_buff, 1);
			dbg_print("lut page %d dat:", nand_tst.lut_page_rd_req);
			dbg_u32_print("\n", (void *)nand_tst.rd_buff, 512, 16);
			nand_tst.lut_page_rd_req = INT32_MAX;
		}

		//全盘写入
		if (nand_tst.nand_fill_req)
		{
			nand_lut_write_all();
			nand_tst.nand_fill_req = 0;
		}

		//全盘读取
		if (nand_tst.nand_read_req)
		{
			nand_lut_read_init(nlut);
			nand_lut_read_all();
			nand_tst.nand_read_req = 0;
		}

		//flt mode
		if (3 == nand_tst.check_req)
		{
			nand_lut_check();
			nand_tst.check_req = 0;
		}

		//重建LUT表测试
		if (nand_tst.lut_req)
		{
			nand_tst.lut_req = 0;
			nand_lut_creat(nlut);
		}

		if (nand_tst.quit)
			break;
	}

	free(nand_tst.wr_buff);
	free(nand_tst.rd_buff);
	free(nand_tst.dw_buff);
	nand_lut_close(nlut);
}

/*------------------------------------------------------------------------------*
* nand read check
*-------------------------------------------------------------------------------*/
void nand_lut_write_all(void)
{
	flash_dbg_print("nand fill start \n");
	for (nand_tst.blocks = 0; nand_tst.blocks < nand_dev.logic_block_amount; nand_tst.blocks++)
	{
		nand_lut_block_write(nlut, nand_tst.blocks, nand_fill_dat_get);

		if (nand_tst.nand_fill_req == 2)
		{
			break;
		}

		if (0 == (nand_tst.blocks & 0x3F))
		{
			flash_dbg_print("nand fill %d %% \n", nand_tst.blocks * 100 / nand_dev.logic_block_amount);
		}
	}

	flash_dbg_print("nand fill end \n");
}

/*------------------------------------------------------------------------------*
* nand read check
*-------------------------------------------------------------------------------*/
void nand_lut_read_all(void)
{
	uint32_t tm_start;

	flash_dbg_print("nand read all start \n");

	tm_start = app_tick_get();

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.logic_block_amount * nand_dev.page_per_block; nand_tst.pages++)
	{

		nand_lut_page_read(nlut, nand_tst.pages, nand_tst.rd_buff, 1);

		if (nand_tst.nand_read_req == 2)
		{
			break;
		}
	}

	nand_tst.opt_time = app_tick_get() - tm_start;

	flash_dbg_print("nand read all end, time used : %d \n", nand_tst.opt_time);
}

/*------------------------------------------------------------------------------*
* nand lut check
*-------------------------------------------------------------------------------*/
void nand_lut_check(void)
{
	uint32_t dats = nand_tst.dat = 9;
	uint32_t tick, errcnt = 0;

	tick = app_tick_get();
	flash_dbg_print("nand lut fill start @ %10d\n", tick);

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		if (nand_tst.pages % nand_dev.page_per_block == 0)
		{
			//nand_lut_block_write(nlut, nand_tst.pages / nand_dev.page_per_block, nand_check_dat_get);
			nand_dev.api.block_write(nand_tst.pages / nand_dev.page_per_block, nand_check_dat_get);
			nand_tst.dat += nand_dev.page_per_block * nand_dev.page_main_size;

			if (0 == (nand_tst.pages & 0x0FFF))
			{
				flash_dbg_print("nand lut fill %d %% \n", nand_tst.pages * 100 / (nand_dev.page_per_block * nand_dev.logic_block_amount));
			}
		}
	}

	flash_dbg_print("nand lut fill end @ %10d , time used: %d ms\n", app_tick_get(), app_tick_get() - tick);
	tick = app_tick_get();
	nand_tst.dat = dats;
	nand_lut_read_init(nlut);

	for (nand_tst.pages = 0; nand_tst.pages < nand_dev.page_per_block * nand_dev.logic_block_amount; nand_tst.pages++)
	{
		//nand_lut_page_read(nlut, nand_tst.pages, nand_tst.dw_buff[0].v, 1);
		nand_dev.api.page_read(nand_tst.pages, nand_tst.dw_buff[0].v, 1);

		for (int index = 0; index < nand_dev.page_main_size / 4; index++)
		{
			if (nand_tst.dw_buff[index].uVal != nand_tst.dat)
			{
				nand_tst.err++;
			}
			nand_tst.dat += 4;
		}

		if (0 == (nand_tst.pages & 0x0FFF))
		{
			flash_dbg_print("nand lut check %d %% \n", nand_tst.pages * 100 / (nand_dev.page_per_block * nand_dev.logic_block_amount));
		}

		if (nand_tst.err)
		{
			flash_dbg_print("nand lut check err cnt : %d @ page %x \n", nand_tst.err, nand_tst.pages);
			errcnt += nand_tst.err;
			nand_tst.err = 0;
		}
	}

	dbg_msg_zero(errcnt, "nand lut check err none\n")

	flash_dbg_print("nand lut check end @ %10d , time used: %d ms\n", app_tick_get(), app_tick_get() - tick);
}

void lut_read_req(int32_t page)
{
	nand_tst.lut_page_rd_req = page;
}

void lut_write_req(int32_t mode)
{
	nand_tst.check_req = 3;
}